Skip to content

Add LLVM 23, drop LLVM 20, and stop emitting broken device debug info - #1419

Open
pvelesko wants to merge 22 commits into
mainfrom
2026-08-03-llvm23-patches
Open

Add LLVM 23, drop LLVM 20, and stop emitting broken device debug info#1419
pvelesko wants to merge 22 commits into
mainfrom
2026-08-03-llvm23-patches

Conversation

@pvelesko

@pvelesko pvelesko commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes CI. Every PR has been red since 2026-07-31 because device debug info is enabled on toolchains that cannot emit it correctly.

Cause

Merging #1414 promoted 0002-preserve-device-debug-info.patch into the tracked patch set. That patch deletes DebugInfoKind = NoDebugInfo from HIPSPVToolChain::adjustDebugInfoKind, which was the only thing keeping device debug info off. Combined with the unconditional -gdwarf-4 in CMakeLists.txt, every build emits device debug info, Release included.

Both SPIR-V producers emit invalid SPIR-V when they do. The translator emits a cyclic forward reference in DebugTypeComposite Parent; the LLVM 22 backend emits DebugTypePointer with a DebugInfoNone base type. Measured with spirv-val on LLVM 22.1.8 and on upstream main. IGC hits an internal compiler error on the result, which is why whole test suites segfault.

The cycle is not fixable by reordering. The spec answer, SPV_KHR_relaxed_extended_instruction, is rejected by every driver we use: Intel dGPU, iGPU and CPU OpenCL, POCL, Arc B570, AMD rusticl, and Mali.

Changes

LLVM 21 and 22 stop applying 0002-preserve-device-debug-info.patch, so stock clang forces NoDebugInfo again and no device debug info is generated. This is the pre-#1414 behaviour that worked for years, and it retires #1004 on those versions: that path was emitting SPIR-V that spirv-val rejects, and only appeared to work on Aurora because its IGC tolerates malformed debug info where Arc ICEs.

LLVM 23 is added, pinned to llvmorg-23.1.0-rc2, with a backport of llvm#213052 which adds in-tree HIPSPV SPIR-V backend support. That backport had to be re-authored against rc2 rather than applied, because upstream assumes llvm#210504 and two extensions that are not on release/23.x. Only two LLVM patches are needed on 23: the other three are already upstream there.

On 23 the debug-info patch is kept but gated on useIntegratedBackend(), so -g is honoured only when the integrated backend is actually in use and a user passing -fno-integrated-objemitter still gets the strip.

LLVM 20 is dropped. Supported set becomes 21, 22, 23.

Why one llvm/23.0 module and not a -native/-translator pair

With llvm#213052 patched in, a single toolchain built with the SPIRV target serves both producers: the translator is built either way, and the producer is chosen per compilation by -f[no-]integrated-objemitter. So --variant becomes a cmake flag rather than a different module. This halves LLVM build time on every CI machine.

Verification

Device debug info was measured working on all five device and backend combinations with the integrated backend on LLVM 24: dgpu-l0, dgpu-ocl, igpu-l0, igpu-ocl, cpu-ocl. All four LLVM 23 patches apply with zero fuzz to a pristine llvmorg-23.1.0-rc2 and to llvm_release_230, and HIPSPV.cpp and SPIRVSubtarget.cpp compile with them in place. A full LLVM 23 build through configure_llvm.sh --version 23 is running as of opening this.

Note the lanes that module load an installed toolchain will still fail on this PR, because they resolve the currently installed compilers. They go green once this merges and install-llvm-on-main promotes the rebuilt toolchains. The lanes to judge this PR by are the Test LLVM Patches ones, which build and test against the freshly staged toolchain.

Depends on #1417, already merged, which stops install-llvm-on-main destroying a live toolchain when no build is staged.

pvelesko added 11 commits August 3, 2026 13:24
Both SPIR-V producers reachable on LLVM 21 and 22 emit debug info that
spirv-val rejects. The SPIRV-LLVM-Translator encodes DebugTypeComposite
with a Parent operand, which forms a genuinely cyclic forward reference,
and the LLVM 22 SPIR-V backend emits DebugTypePointer with a
DebugInfoNone base type. IGC then reports an internal error on Arc.

0002-preserve-device-debug-info was the only thing removing the
DebugInfoKind = NoDebugInfo guard from HIPSPVToolChain::adjustDebugInfoKind,
so dropping the patch restores stock clang's behaviour: no DICompileUnit
is generated at all and nothing downstream can orphan debug metadata.
Its other hunk (SPV_KHR_non_semantic_info / SPV_INTEL_optnone /
nonsemantic-shader-200 under -g) is dead once debug info is off.

Device debug info is delivered on LLVM 23 instead, where the in-tree
SPIR-V backend avoids the cycle structurally.

Refs: #1004
LLVM 23 is pinned to the upstream tag llvmorg-23.1.0-rc2 plus two
patches; all four patches were verified to apply with git apply against
a pristine rc2 checkout and llvm_release_230, and the patched
HIPSPV.cpp and SPIRVSubtarget.cpp compile.

0001 backports llvm/llvm-project#213052 (merged 2026-07-31 as
7ef0ca2b13f9, a reland of #206910), which landed after release/23.x was
cut. It makes the HIPSPV toolchain emit device SPIR-V with the in-tree
SPIR-V backend by default and fall back to llvm-spirv under
-fno-integrated-objemitter or when the SPIR-V target was not built. The
upstream commit sits on a main that already had the extension list and
the debug-info translator flags; release/23.x has neither, so both are
folded in and a patched 23 matches main exactly.

0002 honors -g for device code, but only when the in-tree backend is the
effective emitter. The translator encodes DebugTypeComposite with a
Parent operand, producing a cyclic forward reference that spirv-val
rejects and IGC mis-handles, so -g must keep being stripped there.

The remaining llvm-21/llvm-22 patches are unnecessary here: the SPIR-V
version and extension selection, static device library unbundling, the
data layout and the macOS Mach-O support are all already in 23.

Refs: #1004
23 maps to the upstream tag llvmorg-23.1.0-rc2 (not release/23.x, which
moves) with the translator branch llvm_release_230 and the patches in
llvm-patches/llvm-23/.

LLVM 23 removed the LLVM_ENABLE_PROJECTS=openmp build mode, so openmp
moves to LLVM_ENABLE_RUNTIMES there. This has to stay conditional:
21 and 22 still expect openmp as a project, and CI hard-fails when
omp.h is missing from the resulting install.
The supported set becomes 21, 22, and 23. Removes llvm-patches/llvm-20/,
the 20 option in configure_llvm.sh, and raises the LLVMCheck.cmake floor
to 21.0.0. Documentation examples that named LLVM 20 are moved to 22.

llvm-patches/README.md kept describing llvm-20 as the reference set every
other version was trimmed down from, so llvm-21 takes that role and its
table is spelled out in full.
Profiling.md claimed the preserve-device-debug-info patch is applied for
every supported LLVM. It is now applied only on 23, and there only when
the in-tree SPIR-V backend is the effective emitter.
…module

LLVM 22 has no in-tree HIPSPV backend support, so its two producers need two
separate toolchains and --variant picks between the llvm/22.0-translator and
llvm/22.0-native modules.

From LLVM 23 on chipStar patches in llvm#213052, so one toolchain built with the
SPIRV target serves both: the translator is built either way and the producer is
chosen per compilation. There is a single llvm/23.0 module and --variant becomes
-DCHIP_LLVM_USE_INTERGRATED_SPIRV instead of a different module name.

Also drop llvm-20 from the version error message.
Adds an LLVM 23 row to all three matrices in test-llvm-patches.yml and removes
the LLVM 20 rows.

LLVM 23 needs only one build. chipStar patches in llvm#213052, so a toolchain
built with the SPIRV target also carries the translator and the producer is
selected per compilation by -f[no-]integrated-objemitter. Hence a single
module-name of 23.0 rather than the -native/-translator pair LLVM 22 needs.

The omp.h guard in step2 resolves lib/clang/23/include/omp.h, which the
LLVM_ENABLE_RUNTIMES=openmp build installs.
configure_llvm.sh no longer accepts --version 20, so presubmit.yml's matrix
still asking for it fails at configure time. Move its three pins to 21, the
new oldest supported version.

Also drop the unit-tests-llvm-20-debug and unit-tests-llvm-20-release jobs from
the x86 workflow. They are workflow_dispatch only so they did not fail on a PR,
but they call unit_tests.sh with llvm-20 and nothing depends on them.
configure_llvm.sh builds LLVM with LLVM_LINK_LLVM_DYLIB=ON and
CMAKE_INSTALL_RPATH pinned to the final install prefix. The staged
(DESTDIR) binaries are therefore thin drivers whose RUNPATH points at
$HOME/install/llvm/<ver>/lib, so a staged clang loads libclang-cpp and
libLLVM from the previously installed toolchain. Since essentially all
driver logic lives in those libraries, the "Build LLVM N and test
chipStar" lanes were exercising the installed compiler while reporting
on the newly patched one.

That makes the workflow structurally unable to validate a change to
llvm-patches/, which is the only reason it exists. It also explains why
these lanes went red without any corresponding change to their inputs:
the installed toolchains changed underneath them.

Verified on meatloaf with the staged LLVM 21 from this branch. The same
clang binary, compiling a HIP device TU with -g:

  default (installed libs): -debug-info-kind=constructor  emitted
  staged libs pinned:       no -debug-info-kind           emitted

Pin the loader to the stage and persist it to the test steps, and fail
the Linux lane loudly if the pin does not take effect rather than
silently validating the wrong toolchain again. The macOS side gets the
same pin as a diagnostic, since its linkage layout is not verified here.
chipStar targets spirv64v1.2, and on LLVM 23 the integrated SPIR-V
backend is the default producer. The backend refuses to emit the
GroupNonUniform* capabilities below SPIR-V 1.3, so any warp-level
primitive kills the whole compilation:

  GroupNonUniformShuffle(65) requires SPIR-V version 1.3 or later
  fatal error: error in backend: Unable to meet SPIR-V requirements
               for this target.

This is the same deviation the SPIRV-LLVM-Translator has carried for
years via spirv-translator/0001-pretend-subgroup-caps-are-spirv-1.2.patch;
the backend simply never had an equivalent, which is why LLVM 23 was the
first configuration to hit it.

Reproduced in isolation on LLVM main, no chipStar involved:

  spirv64v1.2 + sub_group_shuffle -> Unable to meet SPIR-V requirements
  spirv64v1.3 + sub_group_shuffle -> ok

The backend gates these capabilities in two independent places and both
must be relaxed; relaxing only the .td leaves them unavailable and fails
identically. The whole GroupNonUniform family is relaxed because chipStar
emits shuffle, shuffle-relative, ballot, arithmetic min/max and quad ops,
and all of them also require the parent GroupNonUniform capability, which
is itself gated at 1.3.
Commit 0947b0e fixed two occurrences but missed line 136. LLVM 23
removed Constant::isZeroValue(); use isNullValue() which is equivalent.

(cherry picked from commit a127229e5ba19aca93f3bdf4086a0462df926d40)
@pvelesko
pvelesko force-pushed the 2026-08-03-llvm23-patches branch from 3061eb5 to 3db51cc Compare August 5, 2026 07:18
pvelesko added 10 commits August 5, 2026 10:26
LLVM 23's HIPSPV toolchain now defaults to the in-tree SPIR-V backend
when built, so translator mode must request llvm-spirv explicitly.

(cherry picked from commit 9c740105cc6ac1a13f5c7b506d170b661e3dc4e4)
CHIPUninitializeCallOnce accessed PerThreadDefaultQueue (a thread_local
unique_ptr) from an atexit handler. TLS destruction order relative to
atexit is implementation-defined; with clang 23 the unique_ptr is
already destroyed, causing a SEGFAULT on the dangling pointer. Remove
the per-thread queue sync since TLS destruction handles cleanup.

(cherry picked from commit 74aed1ed7363630368295da14337fd4e5642f8e3)
The early-out in HipPrintfToOpenCLPrintfPass treated *any* module with a
single printf use as already-lowered ("only used by _cl_printf"). That is
only true once the pass has created its _cl_print_str helper. A module whose
sole printf call is a genuine, not-yet-lowered call -- e.g. the device-side
__assert_fail printf from spirv_hip.hh, which is the only printf in a kernel
that just uses assert() -- also has exactly one use, so the pass skipped it.

Skipping leaves that printf's format string in a non-constant address space.
The SPIR-V translator must then emit SPV_EXT_relaxed_printf_string_address_space,
which the runtime consumer (IGC on Intel GPUs, the llvm-spirv reverse path on
macOS/pocl) rejects with "InvalidModule ... disabled by --spirv-ext option".
The kernel module fails to load and the assert test hangs (200s timeout).

Only short-circuit when the _cl_print_str helper already exists. With the
helper absent, fall through and lower the printf so its format string ends up
in the constant address space and the extension is no longer required.

Fixes Unit_Assert_Positive_Basic_KernelPass_AssertionTest timing out on
LLVM 21 / 22-native (Linux, Intel Arc B570) and LLVM 22 native/translator
(macOS). Verified end-to-end on a B570: the test now passes and device-side
assert prints its message and aborts correctly.

(cherry picked from commit d043503846518a53727aee182787d1dab2144d9e)
embed_spirv_in_cpp() compiled the rtdevlib bitcode with
--target=spirv64v${MAX_SPIRV_VERSION}-unknown-chipstar. The "chipstar" OS
component selects the HIPSPV toolchain, whose -c action emits LLVM
bitcode for the offload driver to link later rather than SPIR-V. clang
exits 0 and writes the file, so every rtdevlib ".spv" silently contained
LLVM bitcode ("BC\xc0\xde") instead of SPIR-V ("\x03\x02\x23\x07"), and
that bitcode was embedded into libCHIP and handed to the OpenCL driver:

  clLinkProgram failed: -17  CL_LINK_PROGRAM_FAILURE   (Intel GPU)
  clLinkProgram failed: -44  CL_INVALID_PROGRAM        (Intel CPU)
  "Device library link step failed."

Measured with the LLVM 23 toolchain:

  --target=spirv64v1.2-unknown-chipstar -> 4243c0de  (LLVM bitcode)
  --target=spirv64v1.2                  -> 03022307  (SPIR-V)

Only LLVM 23 is affected: OFFLOAD_TRIPLE has no "chipstar" OS component
before 23, so the rtdevlib was built correctly there. This is why the 21
and 22 lanes are green while every LLVM 23 test that links the rtdevlib
(ballot, atomics, printf) failed.

Verified on meatloaf against the LLVM 23 toolchain: all rtdevlib modules
now carry the SPIR-V magic and pass spirv-val, and the previously failing
ballot and atomics tests pass on both Intel GPU and Intel CPU OpenCL with
no clLinkProgram errors.
IGC's optimizer miscompiles chipStar kernels, which TestSnakeMiscompileO2
catches. The failure is not in chipStar or in LLVM: the same chipStar
build, the same LLVM 23 toolchain and the same SPIR-V module produce the
correct answer on the Intel CPU runtime and on any Intel GPU when IGC's
optimizer is disabled, and the wrong answer otherwise.

Measured, varying only the IGC library:

  IGC 2.36.3 (meatloaf, latest in apt)   Arc A380   FAIL
  IGC 2.38.2 (cupcake stock)             Arc B570   FAIL
  IGC 2.38.2 (cupcake stock)             UHD 770    FAIL
  igc/all-fixes-2026.07.30               Arc A380   PASS
  igc/all-fixes-2026.07.30               Arc B570   PASS
  igc/all-fixes-2026.07.30               UHD 770    PASS
  IGC 2.38.2 + -cl-opt-disable           Arc B570   PASS
  Intel CPU OpenCL runtime               (same spv) PASS

The bug is unfixed in both released IGCs, so load the local build that
carries the fixes. Full dGPU OpenCL suite on meatloaf with LLVM 23 and
this IGC: 100% tests passed, 0 failed out of 974.

The module is installed at ~/modulefiles/igc/all-fixes-2026.07.30 on the
Linux X64 runner and only prepends LD_LIBRARY_PATH and PATH, so it is
scoped to these steps and does not disturb the system driver.
Backport of llvm/llvm-project#206998, merged upstream 2026-07-21 as
b1d21c6d3121, after release/23.x was cut and therefore missing from
llvmorg-23.1.0-rc2.

LLVM 23 runs ExpandVariadics in the SPIR-V backend IR pipeline for
non-shader targets, i.e. OpenCL kernels. Its guard skips SPIR-V builtins
by testing the demangled name against the prefix "printf(", but an
unmangled C printf demangles to plain "printf" with no argument list, so
OpenCL/HIP printf slipped through and had its arguments packed into a
vararg buffer:

  LLVM 22:  %87 = OpExtInst %uint %1 printf %fmt %arg0 %arg1 %arg2
  LLVM 23:  %99 = OpExtInst %uint %1 printf %fmt %vararg_buffer

Device printf then printed garbage and crashed, on Intel dGPU, iGPU and
the Intel CPU OpenCL runtime alike, unaffected by -cl-opt-disable. This
is what cuda-simplePrintf and PrintfDynamic were catching on the LLVM 23
lane; chipStar's own printf lowering is correct and the IR reaching the
backend is identical on LLVM 22 and 23.

Drop this patch once release/23.x picks the fix up.
SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr() bounds its operand
loop by the call's argument count while indexing the callee's declared
parameters:

  for (unsigned OpIdx = 0; OpIdx < CI->arg_size(); OpIdx++)
    ...
    Argument *CalledArg = CalledF->getArg(OpIdx);

For a variadic callee those counts differ and getArg() asserts:

  Function.h:861: llvm::Function::getArg(unsigned):
    Assertion `i < NumArgs && "getArg() out of range!"' failed.

getArg() is only reached for pointer-typed operands that are neither
Instructions nor Arguments, i.e. pointer constants, so triggering it
needs a variadic call whose variadic argument is a constant pointer.
OpenCL printf with a "%s" argument is exactly that: printf declares one
parameter and the call carries several, the extra one pointing at a
string literal.

It does not reproduce while ExpandVariadics lowers printf away before
this pass runs, which is why it only appeared after backporting
llvm/llvm-project#206998 (patch 0004). Reproducer: chipStar's
hip-tests catch/unit/printf/printfFlags_exe.cc, which crashed clang
during hipspv-link.

The same unguarded indexing is present on llvm-project main, so a kernel
calling printf("%s", ...) should assert there too; this needs reporting
upstream rather than being a release-branch gap.
…NEL_DEBUG_INFO

No SPIR-V producer emits debug information that validates: the translator
emits a cyclic DebugTypeComposite reference and the in-tree backend emits a
DebugCompilationUnit whose DWARF version operand is not a 32-bit unsigned
OpConstant. Only IGC on Intel Data Center GPU Max tolerates it, which is what
makes gdb-oneapi work on Aurora.

Drop the debug metadata at the head of the link-time pipeline instead, so the
question is settled in IR where LLVM already has StripDebugInfo(), and set
-DCHIP_KEEP_KERNEL_DEBUG_INFO=ON on Aurora to keep it.
The hip_sycl_interop[_no_buffers] executables contain no SYCL device code; it
all lives in the onemkl_gemm_wrapper libraries that icpx builds. The SYCL
runtime they need is already on the link line twice: INTEL_LIBS ends with
-lsycl, and libonemkl_gemm_wrapper.so carries the MKL and SYCL libraries in
its own DT_NEEDED.

-fsycl is a driver mode switch rather than a -l shortcut, so it routed the
link through clang-linker-wrapper. From LLVM 23 on that wrapper adds
libLLVMSYCL.so, which only exists in an LLVM built with the SYCL project,
and the link failed with cannot find libLLVMSYCL.so.
The old comment said the queue would be cleaned up by its own TLS
destructor, which is true of deallocation but implies a synchronization
that never happens: ~Queue() is empty, ~CHIPQueueOpenCL() only logs, and
~CHIPQueueLevel0() skips finish() deliberately. Say so, and point at
issue #1439 for the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant